/*********************************************************************
*
* Software License Agreement (BSD License)
*
*  Copyright (c) 2010, Heriot-Watt University, UK.
*  All rights reserved.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following conditions
*  are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the Heriot-Watt University nor the names of
*     its contributors may be used to endorse or promote products
*     derived from this software without specific prior written
*     permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
*  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
*  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
*  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
*  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
*  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
*  POSSIBILITY OF SUCH DAMAGE.
*
*  Author: Joel Cartwright
*
*********************************************************************/

#ifndef MATCHMAKER_SERVICE_STATUS_H_
#define MATCHMAKER_SERVICE_STATUS_H_

#include <matchmaker_msgs/Advert.h>

namespace matchmaker {

// Forward class declaration.
class ServiceStatus;
typedef std::vector<ServiceStatus> V_ServiceStatus;
typedef boost::shared_ptr<V_ServiceStatus> V_ServiceStatusPtr;

/**
 * @brief Holds a status value of a service; a convenience wrapper for an enum
 */
class ServiceStatus
{
public:
  /** Enum of possible status values, mirroring those in the Advert msg */
  typedef enum {

    ERROR = matchmaker_msgs::Advert::STATUS_ERROR,
    REMOVED = matchmaker_msgs::Advert::STATUS_REMOVED,
    UNMET_DEPENDENCIES = matchmaker_msgs::Advert::STATUS_UNMET_DEPENDENCIES,
    AVAILABLE = matchmaker_msgs::Advert::STATUS_AVAILABLE,
    ACTIVE = matchmaker_msgs::Advert::STATUS_ACTIVE,
    ACTIVE_BUSY = matchmaker_msgs::Advert::STATUS_ACTIVE_BUSY,

  } Enum;

  /**
   * @brief Constructor, starts with REMOVED status
   */
  ServiceStatus()
  {
    value_ = REMOVED;
  }

  /**
   * @brief Copy constructor
   * @param rhs instance to copy
   */
  ServiceStatus(const ServiceStatus& rhs)
  {
    value_ = rhs.value_;
    extraStatus_ = rhs.extraStatus_;
  }

  /**
   * Constructor
   * @param status initial status value
   */
  ServiceStatus(Enum status)
  {
    value_ = status;
  }

  /**
   * Constructor
   * @param status initial status value
   */
  ServiceStatus(Enum status, const std::string &extraStatus)
  {
    value_ = status;
    extraStatus_ = extraStatus;
  }

  /**
   * @brief Set the status value
   * @param status value to set
   */
  void setValue(Enum status)
  {
    value_ = status;
  }

  /**
   * @brief Set the extra status string
   * @param extraStatus string to set
   */
  void setExtraStatus(const std::string &extraStatus)
  {
    extraStatus_ = extraStatus;
  }

  /**
   * @brief Get the status value
   * @return status value
   */
  Enum getValue() const
  {
    return value_;
  }

  /**
   * @brief Get the extra status string
   * @return extra string
   */
  std::string getExtraStatus() const
  {
    return extraStatus_;
  }

  /**
   * @brief Check if the instance has the same status value as another
   * @param rhs other value for comparison
   * @return true if same status value
   */
  bool hasValue(const ServiceStatus& rhs) const
  {
    return value_ == rhs.value_;
  }

  /**
   * @brief Check if the instance has a given status value
   * @param rhsValue status value for comparison
   * @return true if same status value
   */
  bool hasExtraStatus(const std::string &extraStatus) const
  {
    return extraStatus_ == extraStatus;
  }

  /**
   * @brief Check if the instance has a given status value
   * @param rhsValue status value for comparison
   * @return true if same status value
   */
  bool hasValue(int16_t rhsValue) const
  {
    return value_ == rhsValue;
  }

  /**
   * @brief Check if the status indicates that the service is usable now
   * @return true if usable
   */
  bool isUsable() const
  {
    return isUsable(value_);
  }

  /**
   * @brief Check if the status indicates that the service is usable or busy
   * @return true if usable or busy
   */
  bool isUsableOrBusy() const
  {
    return isUsableOrBusy(value_);
  }

  /**
   * @brief Create a human readable form of the status, with number and text
   * @return string form of status
   */
  std::string toString() const
  {
    std::string s = toString(value_);

    if (extraStatus_.length() > 0) {
      s += "(" + extraStatus_ + ")";
    }

    return s;
  }

  /**
   * @brief Static version of isUsable()
   * @param status status value to check
   * @return true if status indicates the associated service is usable
   */
  static bool isUsable(int16_t status)
  {
    return status == AVAILABLE || status == ACTIVE;
  }

  /**
   * @brief Static version of isUsableOrBusy()
   * @param status status value to check
   * @return true if status indicates the associated service is usable or busy
   */
  static bool isUsableOrBusy(int16_t status)
  {
    return status == AVAILABLE || status == ACTIVE || status == ACTIVE_BUSY;
  }

  /**
   * @brief Static version of toString()
   * @return string form of status
   */
  static std::string toString(int16_t status)
  {
    std::stringstream ss;
    ss << status << ":";
    switch(status)
    {
      case matchmaker_msgs::Advert::STATUS_ERROR:
        ss << "ERROR"; break;
      case matchmaker_msgs::Advert::STATUS_REMOVED:
        ss << "REMOVED"; break;
      case matchmaker_msgs::Advert::STATUS_UNMET_DEPENDENCIES:
        ss << "UNMET_DEPENDENCIES"; break;
      case matchmaker_msgs::Advert::STATUS_AVAILABLE:
        ss << "AVAILABLE"; break;
      case matchmaker_msgs::Advert::STATUS_ACTIVE:
        ss << "ACTIVE"; break;
      case matchmaker_msgs::Advert::STATUS_ACTIVE_BUSY:
        ss << "ACTIVE_BUSY"; break;
      default:
        ss << "INVALID"; break;
    }

    return ss.str();
  }

  /**
   * @brief Less than comparison with another ServiceStatus
   * @param rhs right hand side of comparison
   * @return true if value of lhs < rhs
   */
  bool operator<(const ServiceStatus& rhs) const
  {
    if (value_ < rhs.value_ || (value_ == rhs.value_ && extraStatus_ < rhs.extraStatus_)) {
      return true;
    }
    else {
      return false;
    }
  }

  /**
   * @brief Less than comparison with an integer status value
   * @param rhsInt value to compare with
   * @return true if value of lhs < rhs
   */
  bool operator<(int16_t rhsInt) const
  {
    return value_ < rhsInt;
  }

  /**
   * @brief Test for equality with another ServiceStatus
   *
   * @param rhs value to compare with
   * @return true if status values are equal
   */
  bool operator==(const ServiceStatus& rhs) const
  {
    return value_ == rhs.value_ && extraStatus_ == rhs.extraStatus_;
  }

  /**
   * @brief Test for equality with an integer status value
   * @param rhsInt value to compare with
   * @return true if status values are equal
   */
  bool operator==(int16_t rhsInt) const
  {
    return value_ == rhsInt;
  }

  /**
   * @brief Test for status value inequality with another ServiceStatus
   *
   * @param rhs value to compare with
   * @return true if status values are not equal
   */
  bool operator!=(const ServiceStatus& rhs) const
  {
    return value_ != rhs.value_ || extraStatus_ != rhs.extraStatus_;
  }

  /**
   * @brief Test for inequality with an integer status value
   * @param rhsInt value to compare with
   * @return true if status values are not equal
   */
  bool operator!=(int16_t rhsInt) const
  {
    return value_ != rhsInt;
  }

private:
  /** Service status value */
  Enum value_;
  /** Extra service status string */
  std::string extraStatus_;
};

} // namespace matchmaker

#endif  /* MATCHMAKER_SERVICE_STATUS_H_ */
